package io.g740.service;

import com.mysql.cj.util.StringUtils;
import io.g740.dto.*;
import io.g740.util.Constants;
import io.g740.util.ServiceUtil;
import io.g740.util.SqlUtil;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.create.table.ColumnDefinition;
import net.sf.jsqlparser.statement.create.table.CreateTable;
import net.sf.jsqlparser.statement.create.table.Index;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 表同步服务类
 *
 * @author YunKaiLuo
 * @date 2024/10/21
 */
public class TableMigrationService {
    public static final String TAG = "【数据迁移】";
    private static final Logger log = LoggerFactory.getLogger(TableMigrationService.class);
    private MigrationConfig migrationConfig;
    /**
     * 源数据库信息
     */
    private TableInfoDto sourceTableInfo;
    /**
     * 目标数据库信息
     */
    private TableInfoDto targetTableInfo;
    /**
     * 目标表与源表的字段映射关系
     */
    private List<FieldMappingDto> fieldMappingDtos;

    public TableMigrationService() {
    }

    public TableMigrationService(MigrationConfig config) {
        this.migrationConfig = config;
        this.sourceTableInfo = config.getSourceTableInfo();
        this.targetTableInfo = config.getTargetTableInfo();
        this.fieldMappingDtos = config.getFieldMappingDtos();
    }

    public void migration() {
        MDC.put(Constants.TARGET_TABLE, String.valueOf(sourceTableInfo.getName()));
        DataSourceConfigDto sourceConfigDto = sourceTableInfo.getDataSourceConfig();
        DataSourceConfigDto targetConfigDto = targetTableInfo.getDataSourceConfig();
        try (Connection sourceConn = DriverManager.getConnection(sourceConfigDto.getUrl(),
                sourceConfigDto.getUsername(), sourceConfigDto.getPassword());
             Connection targetConn = DriverManager.getConnection(targetConfigDto.getUrl(),
                     targetConfigDto.getUsername(), targetConfigDto.getPassword())) {
            // 源表不能修改，只能读取信息
            sourceConn.setReadOnly(true);

            /*
             获取源表的字段列并校验
             */
            Map<String, Column> sourceColumnsInfo = SqlUtil.getColumnsInfo(sourceConn, sourceTableInfo.getName());
            Map<String, FieldMappingDto> fieldMappingMap = fieldMappingDtos.stream().collect(Collectors.toMap(FieldMappingDto::getSourceColumnName
                    , Function.identity()));
            ServiceUtil.validateColumn(sourceColumnsInfo, fieldMappingMap.keySet());

            // 是否存在目标表单
            final boolean existsTargetTable = SqlUtil.existsTable(targetConn, targetTableInfo.getName());

            /*
             下面一大段逻辑是通过解析源表单语法树，新建目标表
             */
            if (!existsTargetTable) {
                // 新建表单
                CreateTableInfoDto createStatement = SqlUtil.getCreateStatement(sourceConn, sourceTableInfo.getName());
                if (createStatement != null) {
                    try {
                        CreateTable createTable = (CreateTable) CCJSqlParserUtil.parse(createStatement.getDdl());
                        createTable.getTable().setName(targetTableInfo.getName());
                        // 设置目标表注释
                        if (!SqlUtil.isBlank(targetTableInfo.getComment())) {
                            ServiceUtil.changeOptions(createTable.getTableOptionsStrings(), "COMMENT", StringUtils.quoteIdentifier(targetTableInfo.getComment(), "'", true));
                        }

                        // 修改主键的字段的列名，转换字段名
                        if (null != createTable.getColumnDefinitions()) {
                            Iterator<ColumnDefinition> iterator = createTable.getColumnDefinitions().iterator();
                            while (iterator.hasNext()) {
                                ColumnDefinition next = iterator.next();
                                String columnName = StringUtils.unQuoteIdentifier(next.getColumnName(), "`");
                                FieldMappingDto fieldMappingDto = fieldMappingMap.get(columnName);
                                if (fieldMappingDto != null) {
                                    next.withColumnName(StringUtils.quoteIdentifier(fieldMappingDto.getTargetColumnName(), true));
                                } else {
                                    if (!migrationConfig.isDefaultMigrationOfAllFields()) {
                                        iterator.remove();
                                    }
                                }
                            }
                        }

                        // 修改索引，转换索引中的字段名
                        if (null != createTable.getIndexes()) {
                            Iterator<Index> iterator = createTable.getIndexes().iterator();
                            while (iterator.hasNext()) {
                                Index next = iterator.next();
                                List<Index.ColumnParams> columns = next.getColumns();
                                for (int i = 0; i < columns.size(); i++) {
                                    Index.ColumnParams columnParams = columns.get(i);
                                    String columnName = StringUtils.unQuoteIdentifier(columnParams.getColumnName(), "`");
                                    FieldMappingDto fieldMappingDto = fieldMappingMap.get(columnName);
                                    if (fieldMappingDto != null) {
                                        columns.set(i,
                                                new Index.ColumnParams(
                                                        StringUtils.quoteIdentifier(fieldMappingDto.getTargetColumnName(), true),
                                                        columnParams.getParams()
                                                ));
                                    } else {
                                        if (!migrationConfig.isDefaultMigrationOfAllFields()) {
                                            iterator.remove();
                                        }
                                    }
                                }
                            }
                        }
                        log.debug(TAG + "输出建表语句：{}", createTable);
                        SqlUtil.execute(targetConn, createTable.toString());
                    } catch (JSQLParserException e) {
                        e.printStackTrace();
                    }
                }
            }
            // 存在
            /*
            TODO 将数据从一个表导入另一个表
             */
            ServiceUtil.migrateData(migrationConfig, sourceConn, targetConn);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            MDC.remove(Constants.TARGET_TABLE);
        }
    }


}
